package com.test.auth_db_auto.config.oauth;

import com.test.auth_db_auto.config.MyTokenEnhancer;
import com.test.auth_db_auto.filter.BootBasicAuthenticationFilter;
import com.test.auth_db_auto.support.MyUserDetailsService;
import com.test.auth_db_auto.support.oauth2.BootClientDetailService;
import com.test.auth_db_auto.support.oauth2.BootOAuth2WebResponseExceptionTranslator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;

/**
 * 授权服务
 * @ Author: .Mr
 * @ ClassName Oauth2Config
 * @ Description TODO
 * @ date 2021/12/20 10:25
 * @ Version 1.0
 */
@Configuration
//开启授权服务
@EnableAuthorizationServer
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {


    private AuthenticationManager authenticationManager;
    private MyUserDetailsService myUserDetailsService;
    private TokenStore tokenStore;
    private JwtAccessTokenConverter converter;
    private BootOAuth2WebResponseExceptionTranslator webResponseExceptionTranslator;
    private AuthenticationEntryPoint authenticationEntryPoint;
    private MyTokenEnhancer tokenEnhancer;
    private BootClientDetailService clientDetailsService;
    private PasswordEncoder passwordEncoder;

    @Autowired(required = false)
    public Oauth2Config(AuthenticationManager authenticationManager
            ,MyUserDetailsService myUserDetailsService
            ,TokenStore tokenStore
            ,JwtAccessTokenConverter converter
            ,BootOAuth2WebResponseExceptionTranslator webResponseExceptionTranslator
            ,AuthenticationEntryPoint authenticationEntryPoint
            ,MyTokenEnhancer tokenEnhancer
            ,BootClientDetailService clientDetailsService
            ,PasswordEncoder passwordEncoder){
        this.authenticationManager=authenticationManager;
        this.myUserDetailsService=myUserDetailsService;
        this.tokenStore=tokenStore;
        this.converter=converter;
        this.webResponseExceptionTranslator=webResponseExceptionTranslator;
        this.authenticationEntryPoint=authenticationEntryPoint;
        this.tokenEnhancer=tokenEnhancer;
        this.clientDetailsService=clientDetailsService;
        this.passwordEncoder=passwordEncoder;
    }

    /**
     * 定义客户端详细信息
     * */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService);
    }

    /**
     * 用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)，
     * 还有token的存储方式(tokenStore)，如果资源服务和授权服务是在同一个服务中用默认的配置即可，不需要做其他任何的配置，
     * 但是如果资源服务和授权服务不在同一个服务中则需要做一些额外配置。如：访问资源服务器所携带的 Token 需要从授权服务做校验等
     * */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //使用redis保存生成的token
        endpoints.tokenStore(tokenStore)
                .authenticationManager(authenticationManager)
                .userDetailsService(myUserDetailsService) // 不配置会导致token无法刷新
//                .tokenServices(defaultTokenServices())
                //往返回的令牌信息中添加额外信息
                .tokenEnhancer(tokenEnhancer)
                .allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET,HttpMethod.DELETE)
                //允许重复使用refresh token
                .reuseRefreshTokens(false);

        // 判断当前是否使用jwt
        if(!(tokenStore instanceof RedisTokenStore) && this.converter!=null){
            endpoints.accessTokenConverter(converter);
        }

        //自定义异常  处理 ExceptionTranslationFilter 抛出的异常
        endpoints.exceptionTranslator(webResponseExceptionTranslator);
    }


    /*@Bean
    public DefaultTokenServices defaultTokenServices(){
            DefaultTokenServices services = new DefaultTokenServices();
//            services.setTokenEnhancer(tokenEnhancer);
            services.setTokenStore(redisTokenStore());
            // 支持使用refresh token刷新access token
            services.setSupportRefreshToken(true);
            //允许重复使用refresh token
            services.setReuseRefreshToken(false);

//            services.setClientDetailsService(clientDetailsService);

            return  services;
    }*/

    /**
     * 认证服务器的安全配置
     * 用来配置令牌端点(Token Endpoint)的安全约束。
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //.realm(RESOURCE_ID)
                //开启/oauth/token_key验证端口认证权限访问
                .tokenKeyAccess("isAuthenticated()")
                // 开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("isAuthenticated()")
                //允许表单认证
                .allowFormAuthenticationForClients();
        // 自定义异常处理端口
        security.authenticationEntryPoint(authenticationEntryPoint);

        // 客户端认证之前的过滤器
        BootBasicAuthenticationFilter filter = new BootBasicAuthenticationFilter(this.clientDetailsService,this.passwordEncoder);
        security.addTokenEndpointAuthenticationFilter(filter);
    }
}
